package com.example.udpDemo.socket.udp;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.example.udpDemo.enums.*;
import com.example.udpDemo.socket.event.NettyEventPublisher;
import com.example.udpDemo.utils.AESUtil;
import com.example.udpDemo.utils.LRC8ChecksumUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author wangdachuan
 * @version 1.0
 * @date 2023/3/14 23:12
 **/
@Slf4j
@Component
public class UdpServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {

    @Override
    protected void messageReceived(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramPacket)
            throws Exception {
        List<NettyPacket> nettyPackets = new ArrayList<>();
        String fromAddress = datagramPacket.sender().getHostString() + ":" + datagramPacket.sender().getPort();
        InetSocketAddress sender = datagramPacket.sender();
        ByteBuf buf = datagramPacket.copy().content();
        try {
            byte[] data = new byte[buf.readableBytes()];
            buf.readBytes(data);
            String hexString = ByteBufUtil.hexDump(data);
            log.info("{},发送的数据：{}", fromAddress, hexString.toUpperCase());
            // 将长字符串每两个字符分割成一个字符串数组
            nettyPackets = decode(channelHandlerContext, datagramPacket, data);
            //TODO 下面进行业务代码处理


        } catch (Exception e) {
            log.error("报文解析异常", e);
        }
        if (CollectionUtil.isNotEmpty(nettyPackets)) {
            NettyEventPublisher nettyEventPublisher = SpringUtil.getBean(NettyEventPublisher.class);
            nettyEventPublisher.publishEvent(nettyPackets);
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        super.channelRead(ctx, msg);
    }

    /**
     * 解码
     *
     * @param ctx
     * @auther sjt Administrator
     * @since 2023/12/17 19:50
     */
    protected List<NettyPacket> decode(ChannelHandlerContext ctx, DatagramPacket datagramPacket, byte[] dataByte) {
        String fromAddress = datagramPacket.sender().getHostString() + ":" + datagramPacket.sender().getPort();

        byte startByte = dataByte[0];//起始符 1byte
        byte[] frameLength = Arrays.copyOfRange(dataByte, 1, 3);//帧长度 不包含起始符和结束符 2byte
        byte[] frameData = Arrays.copyOfRange(dataByte, 3, dataByte.length - 1);//按照帧长度取出后续所有字节 不含结束字节
        byte keyByte = dataByte[3];//密钥 1byte
        byte protocolByte = dataByte[4];//协议版本 1byte
        byte controlTypeByte = dataByte[5];//控制符 1byte
        byte deviceSpeciesByte = dataByte[6];//设备种类 1byte
        byte deviceTypeByte = dataByte[7];//设备类型 1byte
        byte[] deviceModelByte = {dataByte[8], dataByte[9]};//设备型号 2byte
        byte[] deviceId = Arrays.copyOfRange(dataByte, 10, 18);//设备 ID 8byte
        byte[] packageByte = Arrays.copyOfRange(dataByte, 18, 20);//包号 2byte
        byte sort = dataByte[20];//帧序号 1byte
        byte isEndFrameByte = dataByte[21];//是否结束帧 1byte
        byte[] timeBytes = Arrays.copyOfRange(dataByte, 22, 29);// 时间戳 yyyyMMddHHmmss 7byte
        byte[] dataLengthByte = Arrays.copyOfRange(dataByte, 29, 31);//数据包有效长度 2byte
        byte[] dataPackage = Arrays.copyOfRange(dataByte, 31, dataByte.length - 2);//数据包
        byte lrc = dataByte[dataByte.length - 2];
        byte endByte = dataByte[dataByte.length - 1];//结束符 1byte

        // 读取头部数据 判断头部字节的内容，这里假设头部内容为固定值
        if (!"5E".equalsIgnoreCase(String.format("%02X", startByte))) {
            // 头部不匹配，可以处理相应的逻辑，例如关闭连接等
            ctx.close();
            return null;
        }
        //结束符
        if (!"5B".equalsIgnoreCase(String.format("%02X", endByte))) {
            log.info("报文结束符不为5B,丢弃当前报文");
            ctx.close();
            return null;
        }
        int length = Integer.parseInt(ByteBufUtil.hexDump(frameLength), 16);
        if (frameData.length != length) {
            log.info("报文长度校验错误，丢弃当前报文");
            ctx.close();
            return null;
        }
        if (lrc != LRC8ChecksumUtil.calculateLRC(Arrays.copyOfRange(dataByte, 1, dataByte.length - 2))) {
            log.info("LRC校验未通过，丢弃当前报文");
            return null;
        }

        List<NettyPacket> nettyPackets = new ArrayList<>();//需要发送的数据

        int keyNo = Integer.parseInt(String.valueOf(keyByte), 16);
        if (1 == keyNo) {
            dataPackage = AESUtil.decrypt("eVeX2g43w!$NrmKi", dataPackage);
            // 下发2、3密钥
        } else if (2 == keyNo) {
            dataPackage = AESUtil.decrypt("ytnpwstqoa395wze", dataPackage);
        } else if (3 == keyNo) {
            dataPackage = AESUtil.decrypt("ixe7xgz0h628jsfq", dataPackage);
        } else {
            log.info("暂不支持的密钥号：{}", keyNo);
            ctx.close();
            return null;
        }
        //根据数据包长度，去掉补充的0
        int dataLength = Integer.parseInt(ByteBufUtil.hexDump(dataLengthByte), 16);
        dataPackage = Arrays.copyOfRange(dataPackage, 0, dataLength);
//        log.info("{}，解密得到数据包：{}", fromAddress, HexUtil.encodeHexStr(dataPackage).toUpperCase());

        if ("00".equalsIgnoreCase(String.format("%02X", dataPackage[0]))) {
            return null;
        }
        int dataNum = Integer.parseInt(ByteBufUtil.hexDump(new byte[]{dataPackage[1]}), 16);//数据块的数量
        byte[] datas = Arrays.copyOfRange(dataPackage, 2, dataPackage.length);//数据块（子标识+数据段）

        StringBuffer dataStr = new StringBuffer();//应答数据包
        if (0x00 == dataPackage[0]) {
            log.info("{},消息类型：{}", fromAddress, "数据结尾");
            return null;
        } else if (0x01 <= dataPackage[0] && dataPackage[0] <= 0x0F) {
            log.info("{},消息类型：{}", fromAddress, "基本信息");
            dataStr.append("01")//数据包-主标识
                    .append("01")//数据包-数据块数量
                    .append("01").append("01")//数据包-数据块
            ;
        } else if (0x20 <= dataPackage[0] && dataPackage[0] <= 0x2F) {
            log.info("{},消息类型：{}", fromAddress, "运行信息");
            dataStr.append("20")//数据包-主标识
                    .append("01")//数据包-数据块数量
                    .append("01").append("01")//数据包-数据块
            ;
        } else if (0x30 <= dataPackage[0] && dataPackage[0] <= 0x3F) {
            log.info("{},消息类型：{}", fromAddress, "查询工作参数应答");
            return null;
        } else if (0x40 <= dataPackage[0] && dataPackage[0] <= 0x4F) {
            log.info("{},消息类型：{}", fromAddress, "设定工作参数应答");
            return null;
        } else if (0x50 <= dataPackage[0] && dataPackage[0] <= 0x5F) {
            log.info("{},消息类型：{}", fromAddress, "实时数据");
            int startInx = 0;
            for (int i = 0; i < dataNum; i++) {
                byte childTag = datas[startInx++];//子标识
                if (0x01 == childTag) {//传感器数据
                    int num = Integer.parseInt(ByteBufUtil.hexDump(new byte[]{datas[startInx++], datas[startInx++]}), 16);// 传感器/事件/控制器数量
                    for (int j = 0; j < num; j++) {
                        byte[] data = Arrays.copyOfRange(datas, startInx + 12 * j, startInx + 12 * (j + 1));//传感器n的数据
                        startInx += 12;
                        NettyPacket nettyPacket = new NettyPacket();
                        nettyPacket.setNettyPacketType(NettyPacketTypeEnum.REAL_TIME_DATA);
                        nettyPacket.setDataType(0);
                        nettyPacket.setSn(HexUtil.encodeHexStr(deviceId)+"_"+HexUtil.encodeHexStr(Arrays.copyOfRange(data, 0, 4)));
                        nettyPacket.setType(GasTypeEnum.getByCode(data[4]));
                        nettyPacket.setDetectObject(DetectObjectEnum.getByCode(Integer.parseInt(ByteBufUtil.hexDump(new byte[]{data[5], data[6]}), 16)));//检测对象
                        nettyPacket.setValue(ByteBufUtil.hexDump(Arrays.copyOfRange(data, 7, 9)));
                        nettyPacket.setUnit(MeasurementUnitEnum.getByCode(data[9]));
                        nettyPacket.setNumPort(Integer.parseInt(ByteBufUtil.hexDump(new byte[]{data[10]}), 16));
                        nettyPacket.setStatus(GasStatusEnum.getByCode(data[11]));//状态
                        nettyPackets.add(nettyPacket);
                    }
                } else if (0x02 == childTag) {//事件数据
                    int num = Integer.parseInt(ByteBufUtil.hexDump(new byte[]{datas[startInx++], datas[startInx++]}), 16);// 传感器/事件/控制器数量
                    for (int j = 0; j < num; j++) {
                        byte[] data = Arrays.copyOfRange(datas, startInx + 8 * j, startInx + 8 * (j + 1));//事件n的数据
                        startInx += 8;
                        NettyPacket nettyPacket = new NettyPacket();
                        nettyPacket.setNettyPacketType(NettyPacketTypeEnum.REAL_TIME_DATA);
                        nettyPacket.setDataType(1);
                        nettyPacket.setType(EventEnum.getByCode(ByteBuffer.wrap(new byte[]{data[0], data[1]}).getInt()));
                        nettyPacket.setSn(ByteBufUtil.hexDump(Arrays.copyOfRange(data, 2, 6)));
                        nettyPacket.setValue(ByteBufUtil.hexDump(Arrays.copyOfRange(data, 6, 8)));
                    }
                } else if (0x03 == childTag) {//控制器信息
                    int num = Integer.parseInt(ByteBufUtil.hexDump(new byte[]{datas[startInx++], datas[startInx++]}), 16);// 传感器/事件/控制器数量
                    for (int j = 0; j < num; j++) {
                        byte[] data = Arrays.copyOfRange(datas, startInx + 7 * j, startInx + 7 * (j + 1));//控制器n的数据
                        startInx += 7;
                        NettyPacket nettyPacket = new NettyPacket();
                        nettyPacket.setNettyPacketType(NettyPacketTypeEnum.REAL_TIME_DATA);
                        nettyPacket.setDataType(2);
                    }
                }
            }
            //应答
            dataStr.append("50")//数据包-主标识
                    .append("01")//数据包-数据块数量
                    .append("01").append("01")//数据包-数据块
            ;
        } else if (0x60 <= dataPackage[0] && dataPackage[0] <= 0x6F) {
            log.info("{},消息类型：{}", fromAddress, "历史数据");
            return null;
        } else {
            log.error("未知的消息主标识");
            return null;
        }

        /**发送应答包**************************************************/
        StringBuilder resStr = new StringBuilder()
                .append("01")//密码编号
                .append(HexUtil.encodeHexStr(new byte[]{protocolByte}))//协议版本号
                .append("18")//控制符
                .append(HexUtil.encodeHexStr(new byte[]{deviceSpeciesByte}))//设备种类
                .append(HexUtil.encodeHexStr(new byte[]{deviceTypeByte}))//设备类型
                .append(HexUtil.encodeHexStr(deviceModelByte))//设备型号
                .append(HexUtil.encodeHexStr(deviceId))//设备id
                .append("0001")//包号
                .append("01")//帧序号
                .append("01")//是否结束帧
                .append(DateUtil.format(new Date(), "yyyyMMddHHmmss"))//时间戳
                ;
        String resDataLength = HexUtil.toHex(dataStr.length() / 2);//数据包长度
        if (resDataLength.length() % 2 == 1) {
            resDataLength = "0" + resDataLength; //补0
        }
        if (resDataLength.length() == 2) {
            resDataLength = "00" + resDataLength;
        }
        //数据包加密
        byte[] encrypt = AESUtil.encrypt("eVeX2g43w!$NrmKi", hexStringToByte(dataStr.toString()));
        resStr.append(resDataLength)//数据包长度
                .append(HexUtil.encodeHexStr(encrypt))//数据包
        ;
        String resFrameLength = Integer.toHexString((resStr.length() / 2) + 1);//计算帧长度 补充校验符 1byte
        if (resFrameLength.length() % 2 == 1) {
            resFrameLength = "0" + resFrameLength; //补0
        }
        if (resFrameLength.length() == 2) {
            resFrameLength = "00" + resFrameLength;
        }
        resStr.insert(0, resFrameLength);//帧长度
        byte resLRC = LRC8ChecksumUtil.calculateLRC(hexStringToByte(resStr.toString()));
        resStr.append(HexUtil.encodeHexStr(new byte[]{resLRC}));//校验符
        resStr.insert(0, "5e");
        resStr.append("5b");
        log.info("发送给 {},应答：{}", fromAddress, resStr.toString().toUpperCase());
        ctx.channel().writeAndFlush(new DatagramPacket(Unpooled.wrappedBuffer(hexStringToByte(resStr.toString())), datagramPacket.sender()));
        return nettyPackets;
    }

    private static byte[] hexStringToByte(String hexString) {
        int len = hexString.length();
        if (len % 2 != 0) {
            throw new IllegalArgumentException("十六进制字符串必须有偶数个字符：" + hexString);
        }
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
                    + Character.digit(hexString.charAt(i + 1), 16));
        }
        return data;
    }

}
